/*
 * Copyright (C) 2003, Velocityme Partnership
 * DBObjectBean.java
 *
 * Created on 26 March 2003, 09:55
 */

package com.velocityme.entity;

import javax.ejb.*;
import javax.naming.*;
import java.rmi.*;
import javax.rmi.*;
import java.util.*;
import com.velocityme.interfaces.*;
import com.velocityme.interfaces.ServiceUnavailableException;
import java.io.ByteArrayOutputStream;
import java.text.DateFormat;
import javax.xml.parsers.*;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * This is a base class bean for all objects in the database and it keeps track
 * of a list of all changes made to the object.
 *
 * @author  Robert Crida Work
 * @ejb.bean
 *           type="CMP"
 *           cmp-version="2.x"
 *           name="DBObject"
 *           jndi-name="ejb/DBObject"
 *           view-type="local"
 * @ejb.transaction type="Required"
 *
 * @ejb.persistence table-name="dbobject"
 *
 * @ejb.util generate="physical"
 *
 * @ejb.finder signature="java.util.Collection findAllUndeleted()"
 *              query="SELECT OBJECT(o) FROM DBObject o WHERE o.isDeleted = FALSE" 
 *
 * @jboss.persistence create-table="true"
 *                    remove-table="false"
 **/
public abstract class DBObjectBean implements EntityBean {

    /**
    * Context set by container
    */
    private javax.ejb.EntityContext m_entityContext;
   
    public void setEntityContext(javax.ejb.EntityContext entityContext) {
        m_entityContext = entityContext;
    }

    public void unsetEntityContext() {
        m_entityContext = null;
    }
  
    /** @ejb.create-method */
    public com.velocityme.interfaces.DBObjectPK ejbCreate() throws CreateException, EJBException {
        try {
            setDBObjectId(generateNewID());
        }
        catch(ServiceUnavailableException e) {
            throw new EJBException(e.getMessage());
        }
       // EJB 2.0 spec says return null for CMP ejbCreate methods.
        return null;
    }

    public void ejbPostCreate() throws CreateException {
        // CMR relations must be set in ejbPostCreate
        setIsDeleted(Boolean.FALSE);
    }

    /**
     * Retrive a unique creation id to use for this bean.  This will end up
     * demarcating this bean from others when it is stored as a record
     * in the database.
     *
     * @return Returns an integer that can be used as a unique creation id.
     *
     * @throws ServiceUnavailableException Indicating that it was not possible
     *                                     to retrieve a new unqiue ID because
     *                                     the service is not available
     **/
    private Integer generateNewID() throws ServiceUnavailableException {
        int lUniqueId = -1;
        try {            
            SequenceGenerator lBean = SequenceGeneratorUtil.getHome().create();
            lUniqueId = lBean.getNextNumber("dbobject","dBObjectId");
            lBean.remove();
        }
        catch ( NamingException ne ) {
            throw new ServiceUnavailableException( "Naming lookup failure: " + ne.getMessage() );
        }
        catch ( CreateException ce ) {
            throw new ServiceUnavailableException( "Failure while creating a generator session bean: " + ce.getMessage() );
        }
        catch ( RemoveException re ) {
            // When the Bean cannot be removed after a while it will be taken back by the container
            // therefore ignore this exception
        }
        catch ( RemoteException rte ) {
            throw new ServiceUnavailableException( "Remote exception occured while accessing generator session bean: " +  rte.getMessage() );
        }
        
        return new Integer(lUniqueId);
    }
    
    /**
     *  This gets a local object from a remote object.
     *  @ejb.interface-method view-type="remote"
     */
    public com.velocityme.interfaces.DBObjectLocal getLocal() {
        return (DBObjectLocal)m_entityContext.getEJBLocalObject();
    }
    
    //=======================================================================
    //
    //                   ATTRIBUTES
    //
    //=======================================================================
      
    /**
     * @ejb.persistence column-name="dBObjectId"
     * @ejb.interface-method view-type="local"
     * @ejb.pk-field 
     */
    public abstract java.lang.Integer getDBObjectId();
    /** @ejb.interface-method view-type="local" */
    public abstract void setDBObjectId(java.lang.Integer dBObjectId);

   /**
     * @ejb.persistence column-name="isDeleted"
     * @ejb.interface-method view-type="local"
     */
    public abstract java.lang.Boolean getIsDeleted();
    /** @ejb.interface-method view-type="local" */
    public abstract void setIsDeleted(java.lang.Boolean isDeleted);

    //=======================================================================
    //
    //                   RELATIONSHIPS
    //
    //=======================================================================
    
    /**
     * @ejb.interface-method view-type="local"
     * @ejb.relation name="DBObject-ChangeDelta"
     *               role-name="DBObject-has-many-ChangeDeltas"
     */
    public abstract java.util.Collection getChangeDeltasLocal();
    /** @ejb.interface-method view-type="local" */
    public abstract void setChangeDeltasLocal(java.util.Collection changeDeltas);
    
    //=======================================================================
    //
    //                   SELECT METHODS
    //
    //=======================================================================
    
    /**
     * @ejb.interface-method view-type="local"
     * @ejb.select query="SELECT OBJECT(o) FROM ChangeDelta o WHERE o.dBObjectLocal = ?1 AND o.changeDeltaTypeLocal = ?2"
     */
    public abstract java.util.Collection ejbSelectChangeDeltaByType(com.velocityme.interfaces.DBObjectLocal p_dBObjectLocal, com.velocityme.interfaces.ChangeDeltaTypeLocal p_typeLocal) throws FinderException;

    //=======================================================================
    //
    //                   MANAGEMENT METHODS
    //
    //=======================================================================
    
    /**
     * Get an xml dump of the complete change history for this DBObject. Compiled
     * from all the change deltas and their items
     *
     * @ejb.interface-method view-type="local"
     */
    public java.lang.String getChangeLogXML() {
        String xmlString = new String();
        try {
            DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document document = parser.newDocument();
            document.appendChild(document.createProcessingInstruction("xml-stylesheet", "type='text/xsl' href='changeLog.xsl'"));
            document.appendChild(document.createElement("ChangeLog"));
            Element rootElement = document.getDocumentElement();
            DateFormat dateFormatter = DateFormat.getDateTimeInstance();

            Collection changeDeltasLocal = getChangeDeltasLocal();
            Iterator cd = changeDeltasLocal.iterator();
            while(cd.hasNext()) {
                ChangeDeltaLocal cdLocal = (ChangeDeltaLocal)cd.next();
                Element cdElement = document.createElement("ChangeDelta");
                cdElement.setAttribute("date", dateFormatter.format(cdLocal.getDate()));
                cdElement.setAttribute("action", cdLocal.getChangeDeltaTypeLocal().getName());
                cdElement.setAttribute("user", cdLocal.getUserLocal().getUserName());
                cdElement.setAttribute("person", cdLocal.getUserLocal().getPersonLocal().getContactableLocal().getNodeLocal().getName());
                Collection itemsLocal = cdLocal.getChangeDeltaItemsLocal();
                Iterator cdi = itemsLocal.iterator();
                while(cdi.hasNext()) {
                    ChangeDeltaItemLocal cdiLocal = (ChangeDeltaItemLocal)cdi.next();
                    Element cdiElement = document.createElement("Item");
                    cdiElement.setAttribute("name", cdiLocal.getChangeDeltaItemTypeLocal().getName());
                    cdiElement.appendChild(document.createTextNode(cdiLocal.getDescription()));
                    cdElement.appendChild(cdiElement);
                }
                rootElement.appendChild(cdElement);
            }
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            Source source = new DOMSource(document);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            Result output = new StreamResult(outputStream);
            transformer.transform(source, output);
            xmlString = outputStream.toString();
        } catch(Exception e) {
            e.printStackTrace();
        }
        return xmlString;
    }
}
